# Check requisite packages are installed.
packages <- c(
"plotly",
"dplyr"
)
for (pkg in packages) {
library(pkg, character.only = TRUE)
}
What do the results look like?
dirViking <- file.path(
getwd(), "LCAB_LawMorton1996-NumericalPoolCommunityScaling"
)
dirVikingResults <- file.path(
dirViking, "results-2021-04"
)
resultFormat <- paste0(
"run-",
"%d", # Combination Number, or CombnNum.
"-",
"%s", # Run Seed.
".RDS"
)
# Copied from LawMorton1996-NumericalPoolCommunityScaling-Calculation.R
# TODO: In the future, make this a separate file that everyone can call...
set.seed(38427042)
basal <- c(3, 10, 30, 100, 300, 1000)
consumer <- c(3, 10, 30, 100, 300, 1000) * 2
events <- (max(basal) + max(consumer)) * 2
runs <- 100
logBodySize <- c(-2, -1, -1, 1) # Morton and Law 1997 version.
parameters <- c(0.01, 10, 0.5, 0.2, 100, 0.1)
# Need to rerun seedsPrep to get the random number generation right for seedsRun
seedsPrep <- runif(2 * length(basal) * length(consumer)) * 1E8
seedsRun <- runif(runs * length(basal) * length(consumer)) * 1E8
paramFrame <- with(list(
b = rep(basal, times = length(consumer)),
c = rep(consumer, each = length(basal)),
s1 = seedsPrep[1:(length(basal) * length(consumer))],
s2 = seedsPrep[
(length(basal) * length(consumer) + 1):(
2 * length(basal) * length(consumer))
],
sR = seedsRun
), {
temp <- data.frame(
CombnNum = 0,
Basals = b,
Consumers = c,
SeedPool = s1,
SeedMat = s2,
SeedRuns = "",
SeedRunsNum = 0,
EndStates = I(rep(list(""), length(b))),
EndStatesNum = 0,
EndStateSizes = I(rep(list(""), length(b)))
)
for (i in 1:nrow(temp)) {
seeds <- sR[((i - 1) * runs + 1) : (i * runs)]
temp$SeedRuns[i] <- toString(seeds) # CSV
temp$SeedRunsNum[i] <- length(seeds)
}
temp$CombnNum <- 1:nrow(temp)
temp
})
# Note: n + 2 end states. Failure to finish, failure to obtain state, and state.
for (i in 1:nrow(paramFrame)) {
resultsList <- list(
"No Run" = 0,
"No State" = 0
)
resultsSize <- list(
"0" = 0
)
seeds <- unlist(strsplit(paramFrame$SeedRuns[i], ', '))
for (seed in seeds) {
fileName <- file.path(
dirVikingResults,
sprintf(resultFormat, paramFrame$CombnNum[i], seed)
)
if (file.exists(fileName)) {
temp <- load(fileName)
temp <- eval(parse(text = temp)) # Get objects.
if (is.data.frame(temp)) {
community <- toString(
temp[[ncol(temp)]][[nrow(temp)]]
)
size <- toString(length(temp[[ncol(temp)]][[nrow(temp)]]))
if (community == "") {
resultsList$`No State` <- resultsList$`No State` + 1
resultsSize$`0` <- resultsSize$`0` + 1
} else if (community %in% names(resultsList)) {
resultsList[[community]] <- resultsList[[community]] + 1
resultsSize[[size]] <- resultsSize[[size]] + 1
} else {
resultsList[[community]] <- 1
if (size %in% resultsSize) {
resultsSize[[size]] <- resultsSize[[size]] + 1
} else {
resultsSize[[size]] <- 1
}
}
} else {
resultsList$`No State` <- resultsList$`No State` + 1
resultsSize$`0` <- resultsSize$`0` + 1
}
} else {
resultsList$`No Run` <- resultsList$`No Run` + 1
resultsSize$`0` <- resultsSize$`0` + 1
}
}
paramFrame$EndStates[[i]] <- resultsList
paramFrame$EndStatesNum[i] <- length(resultsList) - 2 # ! No State, No Run
paramFrame$EndStateSizes[[i]] <- resultsSize
paramFrame$EndStateSizesNum[i] <- length(resultsSize) - 1 # ! 0
}
# X, Y, Basal and Consumer.
# Z = Sizes of the Endstates.
plotScalingData <- data.frame(
CombnNum = rep(paramFrame$CombnNum, paramFrame$EndStatesNum),
Basals = rep(paramFrame$Basals, paramFrame$EndStatesNum),
Consumers = rep(paramFrame$Consumers, paramFrame$EndStatesNum)
)
# Communities
comms <- unlist(lapply(paramFrame$EndStates, names))
freqs <- unlist(paramFrame$EndStates)
freqs <- freqs[comms != "No Run" & comms != "No State"]
comms <- comms[comms != "No Run" & comms != "No State"]
plotScalingData$Communities <- comms
plotScalingData$CommunityFreq <- freqs
# Community Size
temp <- unlist(lapply(strsplit(plotScalingData$Communities, ','), length))
plotScalingData$CommunitySize <- temp
# For usage by the reader.
plotScaling <- plotly::plot_ly(
plotScalingData,
x = ~Basals,
y = ~Consumers,
z = ~CommunitySize
)
plotScaling <- plotly::add_markers(plotScaling)
plotScaling <- plotly::layout(
plotScaling,
scene = list(
xaxis = list(type = "log"),
yaxis = list(type = "log")
)
)
plotScaling
plotScalingData
How do they compare to each other?
# > runif(1) * 1E8
# [1] 82598679
set.seed(82598679)
temp <- load(file.path(
dirViking,
"LawMorton1996-NumericalPoolCommunityScaling-PoolMats.RDS"
))
mats <- eval(parse(text = temp[1]))
pools <- eval(parse(text = temp[2]))
plotScalingData$CommunityAbund <- ""
for (r in 1:nrow(plotScalingData)) {
plotScalingData$CommunityAbund[r] <- with(plotScalingData[r, ], toString(
RMTRCode2::FindSteadyStateFromEstimate(
Pool = pools[[CombnNum]],
InteractionMatrix = mats[[CombnNum]],
Community = Communities,
Populations = rep(100, CommunitySize), # No good guesses here
maxRandVal = 2E4, # 2 * round of the largest value we have seen so far.
MaxAttempts = 1E4,
Verbose = FALSE
)
))
}
Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.Failed to converge after 10000 attempts.
plotScalingData$CommunityProd <- NA
for (r in 1:nrow(plotScalingData)) {
plotScalingData$CommunityProd[r] <- with(plotScalingData[r, ],
RMTRCode2::Productivity(
Pool = pools[[CombnNum]],
InteractionMatrix = mats[[CombnNum]],
Community = Communities,
Populations = CommunityAbund
)
)
}
candidateData <- plotScalingData %>% dplyr::group_by(
CombnNum
) %>% dplyr::mutate(
OtherSteadyStates = dplyr::n() - 1
) %>% dplyr::filter(
OtherSteadyStates > 0
)
candidateData %>% dplyr::select(-CommunityAbund)
islandFUN <- function(i, dat, pool, mat, dmat) {
temp <- dat[i, ]
RMTRCode2::IslandDynamics(
Pool = pool,
InteractionMatrix = mat,
Communities = c(
list(temp$Communities[1]),
rep("", nrow(dmat) - 2),
temp$Communities[2]
),
Populations = c(
list(temp$CommunityAbund[1]),
rep("", nrow(dmat) - 2),
list(temp$CommunityAbund[2])
),
DispersalPool = 0.0001,
DispersalIsland = dmat,
)
}
# For each group,
# For each pair,
# Run Island Dynamics,
# Save the result with its pairing
for (grp in unique(candidateData$CombnNum)) {
candidateDataSubset <- candidateData %>% dplyr::filter(CombnNum == grp)
pairingResults<- combn(
nrow(candidateDataSubset), 2,
islandFUN,
dat = candidateDataSubset,
pool = pools[[grp]],
mat = mats[[grp]],
dmat = matrix(c(0, 1, 1, 0), nrow = 2, ncol = 2),
simplify = FALSE
)
}
for (grp in unique(candidateData$CombnNum)) {
candidateDataSubset <- candidateData %>% dplyr::filter(CombnNum == grp)
pairingResults<- combn(
nrow(candidateDataSubset), 2,
islandFUN,
dat = candidateDataSubset,
pool = pools[[grp]],
mat = mats[[grp]],
dmat = matrix(c(
0, 1, 0, # Island 2 -> 1
1, 0, 1, # Island 1 -> 2, Island 3 -> 2
0, 1, 0 # Island 2 -> 3
), nrow = 3, ncol = 3, byrow = TRUE),
simplify = FALSE
)
}
LS0tDQp0aXRsZTogIlZpa2luZyBSZXN1bHRzLCAyMDIxLTA0Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KLS0tDQoNCmBgYHtyIGxpYnN9DQojIENoZWNrIHJlcXVpc2l0ZSBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkLg0KcGFja2FnZXMgPC0gYygNCiAgInBsb3RseSIsIA0KICAiZHBseXIiDQopDQpmb3IgKHBrZyBpbiBwYWNrYWdlcykgew0KICBsaWJyYXJ5KHBrZywgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KfQ0KYGBgDQoNCiMgV2hhdCBkbyB0aGUgcmVzdWx0cyBsb29rIGxpa2U/DQpgYGB7ciBkaXJzfQ0KZGlyVmlraW5nIDwtIGZpbGUucGF0aCgNCiAgZ2V0d2QoKSwgIkxDQUJfTGF3TW9ydG9uMTk5Ni1OdW1lcmljYWxQb29sQ29tbXVuaXR5U2NhbGluZyINCikNCmRpclZpa2luZ1Jlc3VsdHMgPC0gZmlsZS5wYXRoKA0KICBkaXJWaWtpbmcsICJyZXN1bHRzLTIwMjEtMDQiDQopDQpyZXN1bHRGb3JtYXQgPC0gcGFzdGUwKA0KICAicnVuLSIsIA0KICAiJWQiLCAjIENvbWJpbmF0aW9uIE51bWJlciwgb3IgQ29tYm5OdW0uDQogICItIiwgDQogICIlcyIsICMgUnVuIFNlZWQuDQogICIuUkRTIg0KKQ0KYGBgDQoNCmBgYHtyIHBhcmFtc30NCiMgQ29waWVkIGZyb20gTGF3TW9ydG9uMTk5Ni1OdW1lcmljYWxQb29sQ29tbXVuaXR5U2NhbGluZy1DYWxjdWxhdGlvbi5SDQojIFRPRE86IEluIHRoZSBmdXR1cmUsIG1ha2UgdGhpcyBhIHNlcGFyYXRlIGZpbGUgdGhhdCBldmVyeW9uZSBjYW4gY2FsbC4uLg0Kc2V0LnNlZWQoMzg0MjcwNDIpDQoNCmJhc2FsIDwtIGMoMywgMTAsIDMwLCAxMDAsIDMwMCwgMTAwMCkNCmNvbnN1bWVyIDwtIGMoMywgMTAsIDMwLCAxMDAsIDMwMCwgMTAwMCkgKiAyDQpldmVudHMgPC0gKG1heChiYXNhbCkgKyBtYXgoY29uc3VtZXIpKSAqIDINCnJ1bnMgPC0gMTAwDQoNCmxvZ0JvZHlTaXplIDwtIGMoLTIsIC0xLCAtMSwgMSkgIyBNb3J0b24gYW5kIExhdyAxOTk3IHZlcnNpb24uDQpwYXJhbWV0ZXJzIDwtIGMoMC4wMSwgMTAsIDAuNSwgMC4yLCAxMDAsIDAuMSkNCg0KIyBOZWVkIHRvIHJlcnVuIHNlZWRzUHJlcCB0byBnZXQgdGhlIHJhbmRvbSBudW1iZXIgZ2VuZXJhdGlvbiByaWdodCBmb3Igc2VlZHNSdW4NCnNlZWRzUHJlcCA8LSBydW5pZigyICogbGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikpICogMUU4DQpzZWVkc1J1biA8LSBydW5pZihydW5zICogbGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikpICogMUU4DQpgYGANCg0KYGBge3Igb3JnYW5pc2VQYXJhbXN9DQpwYXJhbUZyYW1lIDwtIHdpdGgobGlzdCgNCiAgYiA9IHJlcChiYXNhbCwgdGltZXMgPSBsZW5ndGgoY29uc3VtZXIpKSwNCiAgYyA9IHJlcChjb25zdW1lciwgZWFjaCA9IGxlbmd0aChiYXNhbCkpLA0KICBzMSA9IHNlZWRzUHJlcFsxOihsZW5ndGgoYmFzYWwpICogbGVuZ3RoKGNvbnN1bWVyKSldLA0KICBzMiA9IHNlZWRzUHJlcFsNCiAgICAobGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikgKyAxKTooDQogICAgICAyICogbGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikpDQogIF0sDQogIHNSID0gc2VlZHNSdW4NCiksIHsNCiAgdGVtcCA8LSBkYXRhLmZyYW1lKA0KICAgIENvbWJuTnVtID0gMCwNCiAgICBCYXNhbHMgPSBiLA0KICAgIENvbnN1bWVycyA9IGMsDQogICAgU2VlZFBvb2wgPSBzMSwNCiAgICBTZWVkTWF0ID0gczIsDQogICAgU2VlZFJ1bnMgPSAiIiwNCiAgICBTZWVkUnVuc051bSA9IDAsDQogICAgRW5kU3RhdGVzID0gSShyZXAobGlzdCgiIiksIGxlbmd0aChiKSkpLA0KICAgIEVuZFN0YXRlc051bSA9IDAsDQogICAgRW5kU3RhdGVTaXplcyA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgoYikpKQ0KICApDQogIGZvciAoaSBpbiAxOm5yb3codGVtcCkpIHsNCiAgICBzZWVkcyA8LSBzUlsoKGkgLSAxKSAqIHJ1bnMgKyAxKSA6IChpICogcnVucyldDQogICAgdGVtcCRTZWVkUnVuc1tpXSA8LSB0b1N0cmluZyhzZWVkcykgIyBDU1YNCiAgICB0ZW1wJFNlZWRSdW5zTnVtW2ldIDwtIGxlbmd0aChzZWVkcykNCiAgfQ0KICB0ZW1wJENvbWJuTnVtIDwtIDE6bnJvdyh0ZW1wKQ0KICB0ZW1wDQp9KQ0KYGBgDQoNCmBgYHtyIGxvYWRSZXN1bHRzfQ0KIyBOb3RlOiBuICsgMiBlbmQgc3RhdGVzLiBGYWlsdXJlIHRvIGZpbmlzaCwgZmFpbHVyZSB0byBvYnRhaW4gc3RhdGUsIGFuZCBzdGF0ZS4NCmZvciAoaSBpbiAxOm5yb3cocGFyYW1GcmFtZSkpIHsNCiAgcmVzdWx0c0xpc3QgPC0gbGlzdCgNCiAgICAiTm8gUnVuIiA9IDAsDQogICAgIk5vIFN0YXRlIiA9IDANCiAgKQ0KICByZXN1bHRzU2l6ZSA8LSBsaXN0KA0KICAgICIwIiA9IDANCiAgKQ0KICBzZWVkcyA8LSB1bmxpc3Qoc3Ryc3BsaXQocGFyYW1GcmFtZSRTZWVkUnVuc1tpXSwgJywgJykpDQogIGZvciAoc2VlZCBpbiBzZWVkcykgew0KICAgIGZpbGVOYW1lIDwtIGZpbGUucGF0aCgNCiAgICAgIGRpclZpa2luZ1Jlc3VsdHMsDQogICAgICBzcHJpbnRmKHJlc3VsdEZvcm1hdCwgcGFyYW1GcmFtZSRDb21ibk51bVtpXSwgc2VlZCkNCiAgICApDQogICAgDQogICAgaWYgKGZpbGUuZXhpc3RzKGZpbGVOYW1lKSkgew0KICAgICAgdGVtcCA8LSBsb2FkKGZpbGVOYW1lKQ0KICAgICAgdGVtcCA8LSBldmFsKHBhcnNlKHRleHQgPSB0ZW1wKSkgIyBHZXQgb2JqZWN0cy4NCiAgICAgIA0KICAgICAgaWYgKGlzLmRhdGEuZnJhbWUodGVtcCkpIHsNCiAgICAgICAgY29tbXVuaXR5IDwtIHRvU3RyaW5nKA0KICAgICAgICAgIHRlbXBbW25jb2wodGVtcCldXVtbbnJvdyh0ZW1wKV1dDQogICAgICAgICkNCiAgICAgICAgc2l6ZSA8LSB0b1N0cmluZyhsZW5ndGgodGVtcFtbbmNvbCh0ZW1wKV1dW1tucm93KHRlbXApXV0pKQ0KICAgICAgICANCiAgICAgICAgaWYgKGNvbW11bml0eSA9PSAiIikgew0KICAgICAgICAgIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgPC0gcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCArIDENCiAgICAgICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgICAgICAgIA0KICAgICAgICB9IGVsc2UgaWYgKGNvbW11bml0eSAlaW4lIG5hbWVzKHJlc3VsdHNMaXN0KSkgew0KICAgICAgICAgIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSA8LSByZXN1bHRzTGlzdFtbY29tbXVuaXR5XV0gKyAxDQogICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSByZXN1bHRzU2l6ZVtbc2l6ZV1dICsgMQ0KICAgICAgICAgIA0KICAgICAgICB9IGVsc2Ugew0KICAgICAgICAgIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSA8LSAxDQogICAgICAgICAgDQogICAgICAgICAgaWYgKHNpemUgJWluJSByZXN1bHRzU2l6ZSkgew0KICAgICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSByZXN1bHRzU2l6ZVtbc2l6ZV1dICsgMQ0KICAgICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIDENCiAgICAgICAgICB9DQogICAgICAgIH0NCiAgICAgIH0gZWxzZSB7DQogICAgICAgIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgPC0gcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCArIDENCiAgICAgICAgcmVzdWx0c1NpemUkYDBgIDwtIHJlc3VsdHNTaXplJGAwYCArIDENCiAgICAgIH0NCiAgICB9IGVsc2Ugew0KICAgICAgcmVzdWx0c0xpc3QkYE5vIFJ1bmAgPC0gcmVzdWx0c0xpc3QkYE5vIFJ1bmAgKyAxDQogICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgIH0NCiAgfQ0KICANCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZXNbW2ldXSA8LSByZXN1bHRzTGlzdA0KICBwYXJhbUZyYW1lJEVuZFN0YXRlc051bVtpXSA8LSBsZW5ndGgocmVzdWx0c0xpc3QpIC0gMiAjICEgTm8gU3RhdGUsIE5vIFJ1bg0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNbW2ldXSA8LSByZXN1bHRzU2l6ZQ0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNOdW1baV0gPC0gbGVuZ3RoKHJlc3VsdHNTaXplKSAtIDEgIyAhIDANCn0NCmBgYA0KDQo8IS0tYGBge3Igc2hvd1Jlc3VsdHN9DQpwYXJhbUZyYW1lWywgYygxOjMsIDg6MTApXQ0KYGBgLS0+DQoNCmBgYHtyIHBsb3QzRH0NCiMgWCwgWSwgQmFzYWwgYW5kIENvbnN1bWVyLg0KIyBaID0gU2l6ZXMgb2YgdGhlIEVuZHN0YXRlcy4NCg0KcGxvdFNjYWxpbmdEYXRhIDwtIGRhdGEuZnJhbWUoDQogIENvbWJuTnVtID0gcmVwKHBhcmFtRnJhbWUkQ29tYm5OdW0sIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtKSwNCiAgQmFzYWxzID0gcmVwKHBhcmFtRnJhbWUkQmFzYWxzLCBwYXJhbUZyYW1lJEVuZFN0YXRlc051bSksDQogIENvbnN1bWVycyA9IHJlcChwYXJhbUZyYW1lJENvbnN1bWVycywgcGFyYW1GcmFtZSRFbmRTdGF0ZXNOdW0pDQopDQoNCiMgQ29tbXVuaXRpZXMNCmNvbW1zIDwtIHVubGlzdChsYXBwbHkocGFyYW1GcmFtZSRFbmRTdGF0ZXMsIG5hbWVzKSkNCmZyZXFzIDwtIHVubGlzdChwYXJhbUZyYW1lJEVuZFN0YXRlcykNCmZyZXFzIDwtIGZyZXFzW2NvbW1zICE9ICJObyBSdW4iICYgY29tbXMgIT0gIk5vIFN0YXRlIl0NCmNvbW1zIDwtIGNvbW1zW2NvbW1zICE9ICJObyBSdW4iICYgY29tbXMgIT0gIk5vIFN0YXRlIl0NCg0KcGxvdFNjYWxpbmdEYXRhJENvbW11bml0aWVzIDwtIGNvbW1zDQpwbG90U2NhbGluZ0RhdGEkQ29tbXVuaXR5RnJlcSA8LSBmcmVxcw0KDQojIENvbW11bml0eSBTaXplDQp0ZW1wIDwtIHVubGlzdChsYXBwbHkoc3Ryc3BsaXQocGxvdFNjYWxpbmdEYXRhJENvbW11bml0aWVzLCAnLCcpLCBsZW5ndGgpKQ0KcGxvdFNjYWxpbmdEYXRhJENvbW11bml0eVNpemUgPC0gdGVtcA0KDQojIEZvciB1c2FnZSBieSB0aGUgcmVhZGVyLg0KDQpwbG90U2NhbGluZyA8LSBwbG90bHk6OnBsb3RfbHkoDQogIHBsb3RTY2FsaW5nRGF0YSwNCiAgeCA9IH5CYXNhbHMsDQogIHkgPSB+Q29uc3VtZXJzLA0KICB6ID0gfkNvbW11bml0eVNpemUNCikNCg0KcGxvdFNjYWxpbmcgPC0gcGxvdGx5OjphZGRfbWFya2VycyhwbG90U2NhbGluZykNCg0KcGxvdFNjYWxpbmcgPC0gcGxvdGx5OjpsYXlvdXQoDQogIHBsb3RTY2FsaW5nLA0KICBzY2VuZSA9IGxpc3QoDQogICAgeGF4aXMgPSBsaXN0KHR5cGUgPSAibG9nIiksDQogICAgeWF4aXMgPSBsaXN0KHR5cGUgPSAibG9nIikNCiAgKQ0KKQ0KDQpwbG90U2NhbGluZw0KYGBgDQpgYGB7ciBwbG90M2REYXRhfQ0KcGxvdFNjYWxpbmdEYXRhDQpgYGANCiMgSG93IGRvIHRoZXkgY29tcGFyZSB0byBlYWNoIG90aGVyPw0KDQpgYGB7ciBsb2FkUG9vbHNNYXRzfQ0KIyA+IHJ1bmlmKDEpICogMUU4DQojIFsxXSA4MjU5ODY3OQ0Kc2V0LnNlZWQoODI1OTg2NzkpDQoNCnRlbXAgPC0gbG9hZChmaWxlLnBhdGgoDQogIGRpclZpa2luZywgDQogICJMYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nLVBvb2xNYXRzLlJEUyINCikpDQptYXRzICA8LSBldmFsKHBhcnNlKHRleHQgPSB0ZW1wWzFdKSkNCnBvb2xzIDwtIGV2YWwocGFyc2UodGV4dCA9IHRlbXBbMl0pKQ0KYGBgDQoNCmBgYHtyIGNvbXB1dGVQb3B1bGF0aW9uc30NCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlBYnVuZCA8LSAiIg0KZm9yIChyIGluIDE6bnJvdyhwbG90U2NhbGluZ0RhdGEpKSB7DQogIHBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlBYnVuZFtyXSA8LSB3aXRoKHBsb3RTY2FsaW5nRGF0YVtyLCBdLCB0b1N0cmluZygNCiAgICBSTVRSQ29kZTI6OkZpbmRTdGVhZHlTdGF0ZUZyb21Fc3RpbWF0ZSgNCiAgICAgIFBvb2wgPSBwb29sc1tbQ29tYm5OdW1dXSwgDQogICAgICBJbnRlcmFjdGlvbk1hdHJpeCA9IG1hdHNbW0NvbWJuTnVtXV0sIA0KICAgICAgQ29tbXVuaXR5ID0gQ29tbXVuaXRpZXMsIA0KICAgICAgUG9wdWxhdGlvbnMgPSByZXAoMTAwLCBDb21tdW5pdHlTaXplKSwgIyBObyBnb29kIGd1ZXNzZXMgaGVyZQ0KICAgICAgbWF4UmFuZFZhbCA9IDJFNCwgIyAyICogcm91bmQgb2YgdGhlIGxhcmdlc3QgdmFsdWUgd2UgaGF2ZSBzZWVuIHNvIGZhci4NCiAgICAgIE1heEF0dGVtcHRzID0gMUU0LA0KICAgICAgVmVyYm9zZSA9IEZBTFNFDQogICAgKQ0KICApKQ0KfQ0KYGBgDQoNCmBgYHtyIGNvbXB1dGVQcm9kdWN0aXZpdHl9DQpwbG90U2NhbGluZ0RhdGEkQ29tbXVuaXR5UHJvZCA8LSBOQQ0KZm9yIChyIGluIDE6bnJvdyhwbG90U2NhbGluZ0RhdGEpKSB7DQogIHBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlQcm9kW3JdIDwtIHdpdGgocGxvdFNjYWxpbmdEYXRhW3IsIF0sIA0KICAgIFJNVFJDb2RlMjo6UHJvZHVjdGl2aXR5KA0KICAgICAgUG9vbCA9IHBvb2xzW1tDb21ibk51bV1dLCANCiAgICAgIEludGVyYWN0aW9uTWF0cml4ID0gbWF0c1tbQ29tYm5OdW1dXSwgDQogICAgICBDb21tdW5pdHkgPSBDb21tdW5pdGllcywgDQogICAgICBQb3B1bGF0aW9ucyA9IENvbW11bml0eUFidW5kDQogICAgKQ0KICApDQp9DQpgYGANCg0KYGBge3IgY29tcHV0ZUNhbmRpZGF0ZXN9DQpjYW5kaWRhdGVEYXRhIDwtIHBsb3RTY2FsaW5nRGF0YSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICBDb21ibk51bQ0KKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgT3RoZXJTdGVhZHlTdGF0ZXMgPSBkcGx5cjo6bigpIC0gMQ0KKSAlPiUgZHBseXI6OmZpbHRlcigNCiAgT3RoZXJTdGVhZHlTdGF0ZXMgPiAwDQopDQpjYW5kaWRhdGVEYXRhICU+JSBkcGx5cjo6c2VsZWN0KC1Db21tdW5pdHlBYnVuZCkNCmBgYA0KDQpgYGB7ciBpc2xhbmRGVU59DQppc2xhbmRGVU4gPC0gZnVuY3Rpb24oaSwgZGF0LCBwb29sLCBtYXQsIGRtYXQpIHsNCiAgICAgIHRlbXAgPC0gZGF0W2ksIF0NCiAgICAgIFJNVFJDb2RlMjo6SXNsYW5kRHluYW1pY3MoDQogICAgICAgIFBvb2wgPSBwb29sLA0KICAgICAgICBJbnRlcmFjdGlvbk1hdHJpeCA9IG1hdCwNCiAgICAgICAgQ29tbXVuaXRpZXMgPSBjKA0KICAgICAgICAgIGxpc3QodGVtcCRDb21tdW5pdGllc1sxXSksDQogICAgICAgICAgcmVwKCIiLCBucm93KGRtYXQpIC0gMiksDQogICAgICAgICAgdGVtcCRDb21tdW5pdGllc1syXQ0KICAgICAgICApLA0KICAgICAgICBQb3B1bGF0aW9ucyA9IGMoDQogICAgICAgICAgbGlzdCh0ZW1wJENvbW11bml0eUFidW5kWzFdKSwNCiAgICAgICAgICByZXAoIiIsIG5yb3coZG1hdCkgLSAyKSwNCiAgICAgICAgICBsaXN0KHRlbXAkQ29tbXVuaXR5QWJ1bmRbMl0pDQogICAgICAgICksDQogICAgICAgIERpc3BlcnNhbFBvb2wgPSAwLjAwMDEsDQogICAgICAgIERpc3BlcnNhbElzbGFuZCA9IGRtYXQsDQogICAgICApDQogICAgfQ0KYGBgDQoNCmBgYHtyIGlzbGFuZE9uZVR3b30NCiMgRm9yIGVhY2ggZ3JvdXAsDQojIEZvciBlYWNoIHBhaXIsDQojIFJ1biBJc2xhbmQgRHluYW1pY3MsDQojIFNhdmUgdGhlIHJlc3VsdCB3aXRoIGl0cyBwYWlyaW5nDQoNCmZvciAoZ3JwIGluIHVuaXF1ZShjYW5kaWRhdGVEYXRhJENvbWJuTnVtKSkgew0KICBjYW5kaWRhdGVEYXRhU3Vic2V0IDwtIGNhbmRpZGF0ZURhdGEgJT4lIGRwbHlyOjpmaWx0ZXIoQ29tYm5OdW0gPT0gZ3JwKQ0KICBwYWlyaW5nUmVzdWx0czwtIGNvbWJuKA0KICAgIG5yb3coY2FuZGlkYXRlRGF0YVN1YnNldCksIDIsIA0KICAgIGlzbGFuZEZVTiwNCiAgICBkYXQgPSBjYW5kaWRhdGVEYXRhU3Vic2V0LCANCiAgICBwb29sID0gcG9vbHNbW2dycF1dLA0KICAgIG1hdCA9IG1hdHNbW2dycF1dLA0KICAgIGRtYXQgPSBtYXRyaXgoYygwLCAxLCAxLCAwKSwgbnJvdyA9IDIsIG5jb2wgPSAyKSwNCiAgICBzaW1wbGlmeSA9IEZBTFNFDQogICkNCn0NCmBgYA0KDQpgYGB7ciBpc2xhbmRPbmVFbXB0eVR3b30NCmZvciAoZ3JwIGluIHVuaXF1ZShjYW5kaWRhdGVEYXRhJENvbWJuTnVtKSkgew0KICBjYW5kaWRhdGVEYXRhU3Vic2V0IDwtIGNhbmRpZGF0ZURhdGEgJT4lIGRwbHlyOjpmaWx0ZXIoQ29tYm5OdW0gPT0gZ3JwKQ0KICBwYWlyaW5nUmVzdWx0czwtIGNvbWJuKA0KICAgIG5yb3coY2FuZGlkYXRlRGF0YVN1YnNldCksIDIsIA0KICAgIGlzbGFuZEZVTiwNCiAgICBkYXQgPSBjYW5kaWRhdGVEYXRhU3Vic2V0LCANCiAgICBwb29sID0gcG9vbHNbW2dycF1dLA0KICAgIG1hdCA9IG1hdHNbW2dycF1dLA0KICAgIGRtYXQgPSBtYXRyaXgoYygNCiAgICAgIDAsIDEsIDAsICMgSXNsYW5kIDIgLT4gMQ0KICAgICAgMSwgMCwgMSwgIyBJc2xhbmQgMSAtPiAyLCBJc2xhbmQgMyAtPiAyDQogICAgICAwLCAxLCAwICAjIElzbGFuZCAyIC0+IDMNCiAgICApLCBucm93ID0gMywgbmNvbCA9IDMsIGJ5cm93ID0gVFJVRSksDQogICAgc2ltcGxpZnkgPSBGQUxTRQ0KICApDQp9DQpgYGANCg==